package org.ttjhome.server;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.OptimisticLockException;
import javax.persistence.RollbackException;

import org.hibernate.exception.ConstraintViolationException;
import org.ttjhome.db.DbThreadLocal;





public class ServiceProxy implements InvocationHandler{

	private static final Logger log = Logger.getLogger(ServiceProxy.class
			.getName());
	private EntityManagerFactory entityMgrFactory;
	private Map<String, Boolean> acceptedMethods = new HashMap<String, Boolean>();
	private Map<String, Boolean> adminOnlyMethods = new HashMap<String, Boolean>();
	
	private HunterServiceImpl realService;
	
	public ServiceProxy(EntityManagerFactory entity,HunterServiceImpl service) {
		this.entityMgrFactory = entity;
		this.realService = service;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		EntityManager mgr = entityMgrFactory.createEntityManager();
		log.info("entityMgrFactory ["+entityMgrFactory+"] manager :["+mgr+"]");
		DbThreadLocal.getInstance().setThreadsBlackboard(mgr);
		EntityTransaction tx = mgr.getTransaction();
		try{
			
			tx.begin();
			Object retVal = null;
			log.info("invoking method=" + method);
			retVal = method.invoke(realService, args);
			tx.commit();
			return retVal;
		}catch (InvocationTargetException e) {
			log.logp(Level.SEVERE, "Proxy", method.getName(),e.getMessage(),e);
		
			if(e.getTargetException() instanceof OptimisticLockException)
				throw new RuntimeException("Your data is not newest, someone else change it already ,please reselect and update");
			else if (e.getCause() instanceof PasswordException){
				throw e.getCause();
			}
			else 
				throw new RuntimeException(e.getCause().getMessage());
		}catch (RollbackException ex){
			if(ex.getCause() instanceof ConstraintViolationException){
				String error =null;
				if(((ConstraintViolationException) ex.getCause()).getSQLException().getNextException()!=null){
					 error = ((ConstraintViolationException) ex.getCause())
					.getSQLException().getNextException().getMessage();
				}else{
					error = ((ConstraintViolationException) ex.getCause())
					.getSQLException().getMessage();
				}
				
				throw new RuntimeException(error);
			}
			log.info("invoking method=" + method+" Error "+ex.getMessage()+ ex.getClass());
			log.logp(Level.SEVERE, "Proxy", method.getName(),ex.getMessage(),ex);
			throw new RuntimeException(ex.getMessage());
		}catch (Throwable t) {
			log.logp(Level.SEVERE, "Proxy", method.getName(),t.getMessage(),t);
			throw new RuntimeException(t.getMessage());
		}
		finally {
			try {
				if (tx.isActive())
					tx.rollback();
				mgr.close();
			} catch (Exception e) {
				log.log(Level.SEVERE, "Could not close entitymanager", e);
			}
			log.info("done invoking method=" + method);
		}
	}
	
}
